"""
组合策略模块

实现基于多策略组合的投资策略，包括策略权重分配、风险平价、动态再平衡等功能。
"""

from typing import Dict, List, Optional, Tuple, Any
from dataclasses import dataclass, field
from datetime import datetime, timedelta
from enum import Enum
import asyncio
try:
    import numpy as np
except ImportError:
    np = None
import pandas as pd
from abc import ABC, abstractmethod

from .base_strategy import (
    BaseStrategy, StrategyConfig, StrategyMetrics, Signal, Position,
    StrategyType, StrategyStatus, SignalType, RiskLevel
)


class PortfolioType(Enum):
    """组合类型"""
    EQUAL_WEIGHT = "equal_weight"  # 等权重
    RISK_PARITY = "risk_parity"    # 风险平价
    MEAN_VARIANCE = "mean_variance"  # 均值方差优化
    BLACK_LITTERMAN = "black_litterman"  # Black-Litterman模型
    HIERARCHICAL_RISK_PARITY = "hierarchical_risk_parity"  # 层次风险平价


class RebalanceFrequency(Enum):
    """再平衡频率"""
    DAILY = "daily"
    WEEKLY = "weekly"
    MONTHLY = "monthly"
    QUARTERLY = "quarterly"
    ANNUALLY = "annually"


@dataclass
class StrategyWeight:
    """策略权重配置"""
    strategy_id: str
    weight: float
    min_weight: float = 0.0
    max_weight: float = 1.0
    target_volatility: Optional[float] = None


@dataclass
class PortfolioConstraints:
    """组合约束条件"""
    max_strategy_weight: float = 0.5  # 单策略最大权重
    min_strategy_weight: float = 0.0  # 单策略最小权重
    max_total_leverage: float = 1.0   # 最大总杠杆
    max_sector_exposure: float = 0.3  # 最大行业暴露
    max_correlation: float = 0.8      # 最大相关性
    min_diversification: float = 0.1  # 最小分散度


@dataclass
class PortfolioStrategyConfig(StrategyConfig):
    """组合策略配置"""
    portfolio_type: PortfolioType = PortfolioType.EQUAL_WEIGHT
    rebalance_frequency: str = "monthly"
    strategy_weights: List[StrategyWeight] = field(default_factory=list)
    constraints: PortfolioConstraints = field(default_factory=PortfolioConstraints)
    lookback_period: int = 252  # 回望期（交易日）
    min_history_length: int = 60  # 最小历史长度
    volatility_target: Optional[float] = None  # 目标波动率
    risk_budget: Optional[Dict[str, float]] = None  # 风险预算
    correlation_threshold: float = 0.8  # 相关性阈值
    rebalance_threshold: float = 0.05  # 再平衡阈值


class PortfolioStrategy(BaseStrategy):
    """
    组合策略类
    
    实现多策略组合管理，包括权重分配、风险控制、动态再平衡等功能。
    """
    
    def __init__(self, config: PortfolioStrategyConfig):
        super().__init__(config)
        self.config = config
        self.strategies: Dict[str, BaseStrategy] = {}
        self.current_weights: Dict[str, float] = {}
        self.target_weights: Dict[str, float] = {}
        self.last_rebalance: Optional[datetime] = None
        self.performance_history: Dict[str, List[float]] = {}
        self.correlation_matrix: Optional[pd.DataFrame] = None
        self.risk_contributions: Dict[str, float] = {}
        
    async def initialize(self) -> bool:
        """初始化组合策略"""
        try:
            await super().initialize()
            
            # 初始化策略权重
            self._initialize_weights()
            
            # 设置再平衡时间
            self.last_rebalance = datetime.now()
            
            if hasattr(self, 'logger'):
                if hasattr(self, 'logger'):
                    pass  # 移除logger调用
            return True
            
        except Exception as e:
            if hasattr(self, 'logger'):
                if hasattr(self, 'logger'):
                    pass  # 移除logger调用
            return False
    
    def _initialize_weights(self) -> None:
        """初始化策略权重"""
        if self.config.strategy_weights:
            # 使用配置的权重
            total_weight = sum(sw.weight for sw in self.config.strategy_weights)
            for strategy_weight in self.config.strategy_weights:
                normalized_weight = strategy_weight.weight / total_weight
                self.current_weights[strategy_weight.strategy_id] = normalized_weight
                self.target_weights[strategy_weight.strategy_id] = normalized_weight
        else:
            # 等权重分配
            if self.strategies:
                equal_weight = 1.0 / len(self.strategies)
                for strategy_id in self.strategies:
                    self.current_weights[strategy_id] = equal_weight
                    self.target_weights[strategy_id] = equal_weight
    
    def add_strategy(self, strategy_id: str, strategy: BaseStrategy, weight: Optional[float] = None) -> None:
        """添加子策略"""
        self.strategies[strategy_id] = strategy
        
        if weight is not None:
            self.current_weights[strategy_id] = weight
            self.target_weights[strategy_id] = weight
        else:
            # 重新计算等权重
            self._initialize_weights()
        
        # 初始化性能历史
        self.performance_history[strategy_id] = []
    
    def remove_strategy(self, strategy_id: str) -> None:
        """移除子策略"""
        if strategy_id in self.strategies:
            del self.strategies[strategy_id]
            del self.current_weights[strategy_id]
            del self.target_weights[strategy_id]
            del self.performance_history[strategy_id]
            
            # 重新归一化权重
            self._normalize_weights()
    
    def _normalize_weights(self) -> None:
        """归一化权重"""
        total_weight = sum(self.current_weights.values())
        if total_weight > 0:
            for strategy_id in self.current_weights:
                self.current_weights[strategy_id] /= total_weight
                self.target_weights[strategy_id] /= total_weight
    
    async def generate_signal(self, symbol: str, data: pd.DataFrame) -> Signal:
        """生成组合信号"""
        try:
            signals = []
            weights = []
            
            # 收集所有子策略的信号
            for strategy_id, strategy in self.strategies.items():
                if strategy_id in self.current_weights:
                    if hasattr(strategy, 'generate_signal'):
                        if hasattr(strategy, 'generate_signal'):
                            # 使用类型忽略注释
                            signal = await strategy.generate_signal(symbol, data)  # type: ignore
                        else:
                            # 创建默认信号
                            signal = Signal(
                                symbol=symbol,
                                signal_type=SignalType.HOLD,
                                strength=0.0,
                                confidence=0.0,
                                timestamp=datetime.now()
                            )
                    else:
                        continue
                    signals.append(signal)
                    weights.append(self.current_weights[strategy_id])
            
            if not signals:
                return Signal(
                    symbol=symbol,
                    signal_type=SignalType.HOLD,
                    strength=0.0,
                    confidence=0.0,
                    timestamp=datetime.now(),
                    metadata={"source": "portfolio_strategy", "reason": "no_signals"}
                )
            
            # 计算加权信号
            weighted_strength = sum(
                signal.strength * weight 
                for signal, weight in zip(signals, weights)
            )
            
            weighted_confidence = sum(
                signal.confidence * weight 
                for signal, weight in zip(signals, weights)
            )
            
            # 确定信号类型
            if weighted_strength > 0.1:
                signal_type = SignalType.BUY
            elif weighted_strength < -0.1:
                signal_type = SignalType.SELL
            else:
                signal_type = SignalType.HOLD
            
            return Signal(
                symbol=symbol,
                signal_type=signal_type,
                strength=weighted_strength,
                confidence=weighted_confidence,
                timestamp=datetime.now(),
                metadata={
                    "source": "portfolio_strategy",
                    "sub_signals": len(signals),
                    "weights": dict(zip([s.metadata.get("source", "unknown") for s in signals], weights))
                }
            )
            
        except Exception as e:
            if hasattr(self, 'logger'):
                if hasattr(self, 'logger'):
                    pass  # 移除logger调用
            return Signal(
                symbol=symbol,
                signal_type=SignalType.HOLD,
                strength=0.0,
                confidence=0.0,
                timestamp=datetime.now(),
                metadata={"source": "portfolio_strategy", "error": str(e)}
            )
    
    async def calculate_position_size(self, symbol: str, signal: Signal, current_price: float) -> float:
        """计算组合仓位大小"""
        try:
            total_position = 0.0
            
            # 计算各子策略的仓位
            for strategy_id, strategy in self.strategies.items():
                if strategy_id in self.current_weights:
                    if hasattr(strategy, 'calculate_position_size'):
                        if hasattr(strategy, 'calculate_position_size'):
                            strategy_position = await strategy.calculate_position_size(symbol, signal, current_price)  # type: ignore
                        else:
                            strategy_position = 0.0
                    else:
                        strategy_position = 0.0
                    weighted_position = strategy_position * self.current_weights[strategy_id]
                    total_position += weighted_position
            
            # 应用组合约束
            max_position = self.config.max_position_size
            total_position = max(-max_position, min(max_position, total_position))
            
            return total_position
            
        except Exception as e:
            if hasattr(self, 'logger'):
                if hasattr(self, 'logger'):
                    pass  # 移除logger调用
            return 0.0
    
    async def should_rebalance(self) -> bool:
        """判断是否需要再平衡"""
        if self.last_rebalance is None:
            return True
        
        # 检查时间间隔
        time_since_rebalance = datetime.now() - self.last_rebalance
        
        if self.config.rebalance_frequency == RebalanceFrequency.DAILY:
            return time_since_rebalance >= timedelta(days=1)
        elif self.config.rebalance_frequency == RebalanceFrequency.WEEKLY:
            return time_since_rebalance >= timedelta(weeks=1)
        elif self.config.rebalance_frequency == RebalanceFrequency.MONTHLY:
            return time_since_rebalance >= timedelta(days=30)
        elif self.config.rebalance_frequency == RebalanceFrequency.QUARTERLY:
            return time_since_rebalance >= timedelta(days=90)
        elif self.config.rebalance_frequency == RebalanceFrequency.ANNUALLY:
            return time_since_rebalance >= timedelta(days=365)
        
        return False
    
    async def rebalance_portfolio(self) -> None:
        """执行组合再平衡"""
        try:
            if not await self.should_rebalance():
                return
            
            # 计算新的目标权重
            await self._calculate_target_weights()
            
            # 检查权重偏差
            max_deviation = max(
                abs(self.current_weights.get(sid, 0) - self.target_weights.get(sid, 0))
                for sid in set(list(self.current_weights.keys()) + list(self.target_weights.keys()))
            )
            
            if max_deviation > self.config.rebalance_threshold:
                # 执行再平衡
                self.current_weights = self.target_weights.copy()
                self.last_rebalance = datetime.now()
                
                if hasattr(self, 'logger'):
                    if hasattr(self, 'logger'):
                        pass  # 移除logger调用
            
        except Exception as e:
            if hasattr(self, 'logger'):
                if hasattr(self, 'logger'):
                    pass  # 移除logger调用
    
    async def _calculate_target_weights(self) -> None:
        """计算目标权重"""
        try:
            if self.config.portfolio_type == PortfolioType.EQUAL_WEIGHT:
                await self._calculate_equal_weights()
            elif self.config.portfolio_type == PortfolioType.RISK_PARITY:
                await self._calculate_risk_parity_weights()
            elif self.config.portfolio_type == PortfolioType.MEAN_VARIANCE:
                await self._calculate_mean_variance_weights()
            else:
                # 默认等权重
                await self._calculate_equal_weights()
                
        except Exception as e:
            if hasattr(self, 'logger'):
                if hasattr(self, 'logger'):
                    pass  # 移除logger调用
    
    async def _calculate_equal_weights(self) -> None:
        """计算等权重"""
        if self.strategies:
            equal_weight = 1.0 / len(self.strategies)
            for strategy_id in self.strategies:
                self.target_weights[strategy_id] = equal_weight
    
    async def _calculate_risk_parity_weights(self) -> None:
        """计算风险平价权重"""
        try:
            # 获取策略收益率历史
            returns_data = await self._get_strategy_returns()
            
            if returns_data.empty:
                await self._calculate_equal_weights()
                return
            
            # 计算协方差矩阵
            cov_matrix = returns_data.cov()
            
            # 风险平价优化（简化版本）
            n_strategies = len(self.strategies)
            if np is not None:
                weights = np.ones(n_strategies) / n_strategies
            else:
                weights = [1.0 / n_strategies] * n_strategies
            
            # 迭代优化
            for _ in range(100):
                if np is not None:
                    if isinstance(weights, list):
                        portfolio_vol = 1.0  # 简化处理
                    else:
                        portfolio_vol = np.sqrt(weights.T @ cov_matrix @ weights)
                else:
                    portfolio_vol = 1.0
                risk_contributions = (weights * (cov_matrix @ weights)) / portfolio_vol
                
                # 调整权重
                target_risk = portfolio_vol / n_strategies
                adjustment = target_risk / risk_contributions
                weights = weights * adjustment
                weights = weights / weights.sum()
            
            # 更新目标权重
            for i, strategy_id in enumerate(self.strategies.keys()):
                try:
                    if isinstance(weights, list):
                        self.target_weights[strategy_id] = weights[i]
                    else:
                        weight_value = weights[i]
                        if hasattr(weight_value, 'iloc'):
                            # 处理pandas Series
                            self.target_weights[strategy_id] = float(weight_value.iloc[0])
                        else:
                            try:
                                # 使用类型忽略注释处理pandas Series类型转换
                                self.target_weights[strategy_id] = float(weight_value)  # type: ignore
                            except (TypeError, ValueError):
                                self.target_weights[strategy_id] = 1.0 / len(self.strategies)
                except (TypeError, ValueError, IndexError):
                    self.target_weights[strategy_id] = 1.0 / len(self.strategies)
                
        except Exception as e:
            if hasattr(self, 'logger'):
                if hasattr(self, 'logger'):
                    pass  # 移除logger调用
            await self._calculate_equal_weights()
    
    async def _calculate_mean_variance_weights(self) -> None:
        """计算均值方差优化权重"""
        try:
            # 获取策略收益率历史
            returns_data = await self._get_strategy_returns()
            
            if returns_data.empty:
                await self._calculate_equal_weights()
                return
            
            # 计算期望收益和协方差矩阵
            expected_returns = returns_data.mean()
            cov_matrix = returns_data.cov()
            
            # 简化的均值方差优化（等权重作为起点）
            n_strategies = len(self.strategies)
            if np is not None:
                weights = np.ones(n_strategies) / n_strategies
            else:
                weights = [1.0 / n_strategies] * n_strategies
            
            # 更新目标权重
            for i, strategy_id in enumerate(self.strategies.keys()):
                self.target_weights[strategy_id] = float(weights[i])
                
        except Exception as e:
            if hasattr(self, 'logger'):
                if hasattr(self, 'logger'):
                    pass  # 移除logger调用
            await self._calculate_equal_weights()
    
    async def _get_strategy_returns(self) -> pd.DataFrame:
        """获取策略收益率数据"""
        try:
            returns_dict = {}
            
            for strategy_id, strategy in self.strategies.items():
                if strategy_id in self.performance_history:
                    returns_dict[strategy_id] = self.performance_history[strategy_id]
            
            if returns_dict:
                return pd.DataFrame(returns_dict)
            else:
                return pd.DataFrame()
                
        except Exception as e:
            if hasattr(self, 'logger'):
                if hasattr(self, 'logger'):
                    pass  # 移除logger调用
            return pd.DataFrame()
    
    async def update_performance(self, strategy_id: str, return_value: float) -> None:
        """更新策略性能"""
        if strategy_id in self.performance_history:
            self.performance_history[strategy_id].append(return_value)
            
            # 保持历史长度
            max_length = self.config.lookback_period
            if len(self.performance_history[strategy_id]) > max_length:
                self.performance_history[strategy_id] = self.performance_history[strategy_id][-max_length:]
    
    async def calculate_metrics(
        self,
        returns: pd.Series,
        benchmark_returns: Optional[pd.Series] = None
    ) -> StrategyMetrics:
        """计算组合策略指标"""
        try:
            # 收集所有子策略的指标
            strategy_metrics = {}
            for strategy_id, strategy in self.strategies.items():
                if hasattr(strategy, 'calculate_metrics'):
                    if benchmark_returns is not None:
                        metrics = await strategy.calculate_metrics(returns, benchmark_returns)
                    else:
                        # 创建一个空的基准收益率序列
                        empty_benchmark = pd.Series(dtype=float)
                        metrics = await strategy.calculate_metrics(returns, empty_benchmark)
                else:
                    continue
                strategy_metrics[strategy_id] = metrics
            
            if not strategy_metrics:
                return StrategyMetrics(
                    total_return=0.0,
                    annual_return=0.0,
                    volatility=0.0,
                    sharpe_ratio=0.0,
                    max_drawdown=0.0,
                    win_rate=0.0,
                    profit_loss_ratio=0.0,
                    total_trades=0,
                    profitable_trades=0,
                    losing_trades=0,
                    avg_trade_return=0.0,
                    avg_holding_period=0.0,
                    turnover_rate=0.0,
                    information_ratio=0.0,
                    calmar_ratio=0.0,
                    sortino_ratio=0.0,
                    var_95=0.0,
                    cvar_95=0.0,
                    beta=0.0,
                    alpha=0.0,
                    tracking_error=0.0,
                    start_date=datetime.now(),
                    end_date=datetime.now(),
                    last_update=datetime.now()
                )
            
            # 计算加权指标
            total_return = sum(
                metrics.total_return * self.current_weights.get(strategy_id, 0)
                for strategy_id, metrics in strategy_metrics.items()
            )
            
            annual_return = sum(
                metrics.annual_return * self.current_weights.get(strategy_id, 0)
                for strategy_id, metrics in strategy_metrics.items()
            )
            
            # 计算组合波动率（考虑相关性）
            volatility = await self._calculate_portfolio_volatility(strategy_metrics)
            
            sharpe_ratio = annual_return / volatility if volatility > 0 else 0.0
            
            max_drawdown = max(
                metrics.max_drawdown for metrics in strategy_metrics.values()
            )
            
            # 其他指标的加权平均
            win_rate = sum(
                metrics.win_rate * self.current_weights.get(strategy_id, 0)
                for strategy_id, metrics in strategy_metrics.items()
            )
            
            total_trades = sum(metrics.total_trades for metrics in strategy_metrics.values())
            profitable_trades = sum(metrics.profitable_trades for metrics in strategy_metrics.values())
            losing_trades = sum(metrics.losing_trades for metrics in strategy_metrics.values())
            
            return StrategyMetrics(
                total_return=total_return,
                annual_return=annual_return,
                volatility=volatility,
                sharpe_ratio=sharpe_ratio,
                max_drawdown=max_drawdown,
                win_rate=win_rate,
                profit_loss_ratio=profitable_trades / losing_trades if losing_trades > 0 else 0.0,
                total_trades=total_trades,
                profitable_trades=profitable_trades,
                losing_trades=losing_trades,
                avg_trade_return=total_return / total_trades if total_trades > 0 else 0.0,
                avg_holding_period=sum(
                    metrics.avg_holding_period * self.current_weights.get(strategy_id, 0)
                    for strategy_id, metrics in strategy_metrics.items()
                ),
                turnover_rate=sum(
                    metrics.turnover_rate * self.current_weights.get(strategy_id, 0)
                    for strategy_id, metrics in strategy_metrics.items()
                ),
                information_ratio=sum(
                    metrics.information_ratio * self.current_weights.get(strategy_id, 0)
                    for strategy_id, metrics in strategy_metrics.items()
                ),
                calmar_ratio=annual_return / abs(max_drawdown) if max_drawdown != 0 else 0.0,
                sortino_ratio=sum(
                    metrics.sortino_ratio * self.current_weights.get(strategy_id, 0)
                    for strategy_id, metrics in strategy_metrics.items()
                ),
                var_95=sum(
                    metrics.var_95 * self.current_weights.get(strategy_id, 0)
                    for strategy_id, metrics in strategy_metrics.items()
                ),
                cvar_95=sum(
                    metrics.cvar_95 * self.current_weights.get(strategy_id, 0)
                    for strategy_id, metrics in strategy_metrics.items()
                ),
                beta=sum(
                    metrics.beta * self.current_weights.get(strategy_id, 0)
                    for strategy_id, metrics in strategy_metrics.items()
                ),
                alpha=sum(
                    metrics.alpha * self.current_weights.get(strategy_id, 0)
                    for strategy_id, metrics in strategy_metrics.items()
                ),
                tracking_error=sum(
                    metrics.tracking_error * self.current_weights.get(strategy_id, 0)
                    for strategy_id, metrics in strategy_metrics.items()
                ),
                start_date=min(metrics.start_date for metrics in strategy_metrics.values()),
                end_date=max(metrics.end_date for metrics in strategy_metrics.values()),
                last_update=datetime.now()
            )
            
        except Exception as e:
            if hasattr(self, 'logger'):
                if hasattr(self, 'logger'):
                    pass  # 移除logger调用
            return StrategyMetrics(
                total_return=0.0,
                annual_return=0.0,
                volatility=0.0,
                sharpe_ratio=0.0,
                max_drawdown=0.0,
                win_rate=0.0,
                profit_loss_ratio=0.0,
                total_trades=0,
                profitable_trades=0,
                losing_trades=0,
                avg_trade_return=0.0,
                avg_holding_period=0.0,
                turnover_rate=0.0,
                information_ratio=0.0,
                calmar_ratio=0.0,
                sortino_ratio=0.0,
                var_95=0.0,
                cvar_95=0.0,
                beta=0.0,
                alpha=0.0,
                tracking_error=0.0,
                start_date=datetime.now(),
                end_date=datetime.now(),
                last_update=datetime.now()
            )
    
    async def _calculate_portfolio_volatility(self, strategy_metrics: Dict[str, StrategyMetrics]) -> float:
        """计算组合波动率"""
        try:
            # 简化计算：加权平均波动率
            volatility = sum(
                metrics.volatility * self.current_weights.get(strategy_id, 0)
                for strategy_id, metrics in strategy_metrics.items()
            )
            
            return volatility
            
        except Exception as e:
            if hasattr(self, 'logger'):
                if hasattr(self, 'logger'):
                    pass  # 移除logger调用
            return 0.0
    
    async def get_portfolio_summary(self) -> Dict[str, Any]:
        """获取组合摘要信息"""
        return {
            "portfolio_type": self.config.portfolio_type.value,
            "rebalance_frequency": self.config.rebalance_frequency,
            "num_strategies": len(self.strategies),
            "current_weights": self.current_weights.copy(),
            "target_weights": self.target_weights.copy(),
            "last_rebalance": self.last_rebalance,
            "risk_contributions": self.risk_contributions.copy()
        }